﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using System.Web;
using Common.Logging;
using Internal;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using TSharp.Core.Aop;
using TSharp.Core.Asm;

namespace System
{
    /// <summary>
    /// AOP容器上下文
    /// <para>by tangjingbo at 2009-11-4 14:37</para>
    /// </summary>
    public abstract class AopContext : IDisposable
    {
        private static readonly IList<IContainerListener> listeners = new List<IContainerListener>
                                                                          {
                                                                              new LateBindConfiguration()
                                                                          };

        private static Func<HttpContextBase> _factory = () =>
                                                            {
                                                                if (HttpContext.Current != null)
                                                                    return new HttpContextWrapper(HttpContext.Current);
                                                                return null;
                                                            };

        private static AopContext _ctx;
        private readonly ILog log = LogManager.GetLogger("AopContext");
        private UnityConfigurationSection _unitySection;

        #region 常量

        /// <summary>
        /// 系统容器key
        /// </summary>
        private const string CACHEKEY_SYSTEM_CONTAINER = "CONTAINER_SYSTEM";

        /// <summary>
        /// 会话容器KEY
        /// </summary>
        private const string CACHEKEY_SESSION_CONTAINER = "CONTAINER_SESSION";

        /// <summary>
        /// 请求容器key
        /// </summary>
        private const string CACHEKEY_REQUSET_CONTAINER = "CONTAINER_REQUEST";

        private const int millisecondsTimeout = 60000;

        /// <summary>
        /// 系统级别容器名称
        /// </summary>
        public static string CONFIG_CONTAINER_NAME_SYSTEM = "system";

        /// <summary>
        /// 会话级别容器名称
        /// </summary>
        public static string CONFIG_CONTAINER_NAME_SESSION = "session";

        /// <summary>
        /// 请求级别容器名称
        /// </summary>
        public static string CONFIG_CONTAINER_NAME_REQUEST = "request";

        #endregion

        /// <summary>
        /// 
        /// <para>by tangjingbo at 2009-10-22 17:48</para>
        /// </summary>
        private AopContext()
        {
        }

        #region 获取和重新创建上下文

        private static readonly object aopCtxLocker = new object();

        /// <summary>
        /// aop上下文
        /// </summary>
        public static AopContext Current
        {
            get
            {
                if (_ctx != null)
                    return _ctx;
                lock (aopCtxLocker)
                {
                    if (_ctx == null)
                        if (Context != null)
                            _ctx = new WebAopContext();
                        else
                            _ctx = new WinAopContext();
                }
                return _ctx;
            }
        }

        /// <summary>
        /// 重新创建上下文，返回原有上下文
        /// </summary>
        /// <returns></returns>
        public static AopContext Reset()
        {
            AopContext origin = _ctx;
            if (Context != null)
                _ctx = new WebAopContext();
            else
                _ctx = new WinAopContext();
            return origin;
        }

        #endregion

        /// <summary>
        /// 获取默认IOC容器
        /// <para>
        /// TODO:根据配置获取默认level容器
        /// </para>
        /// </summary>
        /// <value>The default.</value>
        public static IUnityContainer Default
        {
            get { return Current.Request; }
        }

        /// <summary>
        /// 获取aopcontext中的HttpContextBase
        /// </summary>
        /// <value>The context.</value>
        public static HttpContextBase Context
        {
            get { return _factory(); }
        }

        /// <summary>
        /// unity配置节点
        /// <para>by tangjingbo at 2009-11-4 14:38</para>
        /// </summary>
        /// <value></value>
        internal UnityConfigurationSection UnitySection
        {
            get
            {
                if (_unitySection != null)
                {
                    return _unitySection;
                }
                _unitySection = WebConfigUtil.GetSection<UnityConfigurationSection>("unity");
                return _unitySection;
            }
        }

        #region RootContainer

        /// <summary>
        /// 系统级别容器.web程序存储在HttpAppliction中，应用程序域内多线程共享
        /// <para>by tangjingbo at 2009-11-4 14:37</para>
        /// </summary>
        /// <value></value>
        public abstract IUnityContainer Root { get; }

        #endregion

        #region SessionContainer

        /// <summary>
        /// 会话级别容器。winform对每个线程会有不同的实例，线程内共享，web中session内共享。
        /// </summary>
        /// <value>The session.</value>
        public abstract IUnityContainer Session { get; }

        #endregion

        #region RequestContainer

        /// <summary>
        /// 请求级别容器。winform首先Session子容器，然后返回；web中每次请求有不同实例，请求内共享。
        /// </summary>
        /// <value>The request.</value>
        public abstract IUnityContainer Request { get; }

        #endregion

        /// <summary>
        /// 设置HTTP上下文工厂.
        /// </summary>
        /// <param name="factory">The factory.</param>
        public static void SetHttpContextFactory(Expression<Func<HttpContextBase>> factory)
        {
            _factory = factory.Compile();
            Reset();
        }

        internal IUnityContainer CreateRoot()
        {
            log.Debug("创建 Root");
            UnityContainerElement containerElement = UnitySection.Containers[CONFIG_CONTAINER_NAME_SYSTEM] ??
                                                     UnitySection.Containers.Default;
            var container = new UnityContainer();
            if (containerElement != null)
                containerElement.Configure(container);
            ServiceManager.RootMap.Configure(container);
            return container;
        }

        internal IUnityContainer CreateSession()
        {
            log.Debug("创建 Session");
            UnityContainerElement containerElement = UnitySection.Containers[CONFIG_CONTAINER_NAME_SESSION];
            IUnityContainer container = Root.CreateChildContainer();
            if (containerElement != null)
                containerElement.Configure(container);
            ServiceManager.SessionMap.Configure(container);
            return container;
        }

        internal IUnityContainer CreateRequest()
        {
            log.Debug("创建 Request");
            UnityContainerElement containerElement = UnitySection.Containers[CONFIG_CONTAINER_NAME_REQUEST];
            IUnityContainer container = Session.CreateChildContainer();
            if (containerElement != null)
                containerElement.Configure(container);
            ServiceManager.RequsetMap.Configure(container);
            return container;
        }

        #region web程序

        private class WebAopContext : AopContext
        {
            public override IUnityContainer Root
            {
                get { return GetRoot(Context); }
            }

            public override IUnityContainer Session
            {
                get { return GetSession(Context); }
            }

            public override IUnityContainer Request
            {
                get { return GetRequest(Context); }
            }

            private IUnityContainer GetRequest(HttpContextBase ctx)
            {
                if (ctx.Items == null)
                {
                    return CreateRequest();
                }
                var request = ctx.Items[CACHEKEY_REQUSET_CONTAINER] as IUnityContainer;
                if (request != null)
                    return request;
                //if ((request = ctx.Items[CACHEKEY_REQUSET_CONTAINER] as IUnityContainer) == null)
                ctx.Items[CACHEKEY_REQUSET_CONTAINER] = request = CreateRequest();
                return request;
            }

            //private static object synSession = new object();
            private IUnityContainer GetSession(HttpContextBase ctx)
            {
                if (ctx.Session == null)
                {
                    return CreateSession();
                }
                var container = ctx.Session[CACHEKEY_SESSION_CONTAINER] as IUnityContainer;
                if (container != null)
                    return container;
                //using (var l = new Internal.Lock(synSession))
                //if ((container = ctx.Session[CACHEKEY_SESSION_CONTAINER] as IUnityContainer) == null)
                ctx.Session[CACHEKEY_SESSION_CONTAINER] = container = CreateSession();
                return container;
            }

            //private static object synRoot = new object();
            private IUnityContainer GetRoot(HttpContextBase ctx)
            {
                var container = ctx.Application[CACHEKEY_SYSTEM_CONTAINER] as IUnityContainer;
                if (container != null)
                    return container;
                ctx.Application.Lock();
                try
                {
                    //using (new Internal.Lock(synRoot))
                    if ((container = ctx.Application[CACHEKEY_SYSTEM_CONTAINER] as IUnityContainer) == null)
                        ctx.Application[CACHEKEY_SYSTEM_CONTAINER] = container = CreateRoot();
                }
                finally
                {
                    ctx.Application.UnLock();
                }
                return container;
            }
        }

        #endregion

        #region 桌面程序

        private class WinAopContext : AopContext
        {
            private static readonly object synRequest = new object();
            private static readonly object synSession = new object();
            private static readonly object synRoot = new object();
            private static IUnityContainer container;

            public override IUnityContainer Root
            {
                get { return GetRoot(); }
            }

            public override IUnityContainer Session
            {
                get { return GetSession(); }
            }

            public override IUnityContainer Request
            {
                get { return GetRequest(); }
            }

            private IUnityContainer GetRoot()
            {
                //var container = CallContext.LogicalGetData(CACHEKEY_SYSTEM_CONTAINER) as IUnityContainer;
                if (container != null)
                    return container;
                using (var l = new Lock(synRoot))
                    if (container == null)
                        container = CreateRoot();
                return container;
            }

            private IUnityContainer GetRequest()
            {
                var container = CallContext.GetData(CACHEKEY_REQUSET_CONTAINER) as IUnityContainer;
                if (container != null)
                    return container;
                using (var l = new Lock(synRequest))
                    if ((container = CallContext.GetData(CACHEKEY_REQUSET_CONTAINER) as IUnityContainer) == null)
                        CallContext.SetData(CACHEKEY_REQUSET_CONTAINER, (container = CreateRequest()));
                return container;
            }

            private IUnityContainer GetSession()
            {
                var container = CallContext.GetData(CACHEKEY_SESSION_CONTAINER) as IUnityContainer;
                if (container != null)
                    return container;
                using (new Lock(synSession))
                    if ((container = CallContext.GetData(CACHEKEY_SESSION_CONTAINER) as IUnityContainer) == null)
                        CallContext.SetData(CACHEKEY_SESSION_CONTAINER, (container = CreateSession()));
                return container;
            }
        }

        #endregion

        #region IDisposable 成员

        /// <summary>
        /// 销毁
        /// <para>by tangjingbo at 2009-11-4 14:38</para>
        /// </summary>
        public void Dispose()
        {
            if (Root != null)
            {
                Root.Dispose();
            }
            GC.SuppressFinalize(this);
        }

        #endregion

        #region Nested type: LateBindConfiguration

        private class LateBindConfiguration : IContainerListener
        {
            #region IContainerListener Members

            public void CreateSystemContainer(AopContext ctx, IUnityContainer system)
            {
                ServiceManager.RootMap.Configure(system);
            }

            public void CreateSessionContainer(AopContext ctx, IUnityContainer session)
            {
                ServiceManager.SessionMap.Configure(session);
            }

            public void CreateRequestContainer(AopContext ctx, IUnityContainer request)
            {
                ServiceManager.RequsetMap.Configure(request);
            }

            #endregion
        }

        #endregion
    }

    /// <summary>
    /// 容器创建监听接口
    /// <para>2010/12/29</para>
    /// 	<para>TANGJINGBO</para>
    /// 	<para>tangjingbo</para>
    /// </summary>
    public interface IContainerListener
    {
        /// <summary>
        /// 系统级别容器创建
        /// </summary>
        /// <param name="ctx">The CTX.</param>
        /// <param name="system">The system.</param>
        void CreateSystemContainer(AopContext ctx, IUnityContainer system);

        /// <summary>
        /// 会话级别容器创建
        /// </summary>
        /// <param name="ctx">The CTX.</param>
        /// <param name="session">The session.</param>
        void CreateSessionContainer(AopContext ctx, IUnityContainer session);

        /// <summary>
        /// 请求级别容器创建
        /// </summary>
        /// <param name="ctx">The CTX.</param>
        /// <param name="request">The request.</param>
        void CreateRequestContainer(AopContext ctx, IUnityContainer request);
    }
}

namespace Internal
{
    internal class Lock : IDisposable
    {
        /// <summary>
        /// 默认超时设置
        /// </summary>
        public static int DefaultMillisecondsTimeout = 15000; // 15S

        private object _obj;

        /// <summary>
        /// 构造 
        /// </summary>
        /// <param name="obj">想要锁住的对象</param>
        public Lock(object obj)
        {
            TryGet(obj, DefaultMillisecondsTimeout, true);
        }

        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="obj">想要锁住的对象</param>
        /// <param name="millisecondsTimeout">超时设置</param>
        public Lock(object obj, int millisecondsTimeout)
        {
            TryGet(obj, millisecondsTimeout, true);
        }

        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="obj">想要锁住的对象</param>
        /// <param name="millisecondsTimeout">超时设置</param>
        /// <param name="throwTimeoutException">是否抛出超时异常</param>
        public Lock(object obj, int millisecondsTimeout, bool throwTimeoutException)
        {
            TryGet(obj, millisecondsTimeout, throwTimeoutException);
        }

        /// <summary>
        /// 获取一个值指示在获取排他锁时是否发生超时，如果为true则未在指定对象上获取排它锁。
        /// <para>
        /// by tangjingbo at 2009-2-24 12:23
        /// </para>
        /// </summary>
        /// <value></value>
        public bool IsTimeout
        {
            get { return _obj == null; }
        }

        #region IDisposable Members

        /// <summary>
        /// 销毁，并释放锁
        /// <para>
        /// by tangjingbo at 2009-2-24 12:23
        /// </para>
        /// </summary>
        public void Dispose()
        {
            if (_obj != null)
            {
                Monitor.Exit(_obj);
                _obj = null;
            }
            GC.SuppressFinalize(this);
        }

        #endregion

        private void TryGet(object obj, int millisecondsTimeout, bool throwTimeoutException)
        {
            if (Monitor.TryEnter(obj, millisecondsTimeout))
            {
                _obj = obj;
            }
            else
            {
                if (throwTimeoutException)
                {
                    throw new TimeoutException("试图获取'" + obj + "'的控制权超时(" + millisecondsTimeout +
                                               "毫秒)，请确认锁对象是否与其他线程冲突，或者查看该代码块执行是否过于耗时。");
                }
            }
        }
    }
}